﻿using System;
using System.Diagnostics;
using System.Drawing;
using RaytracerLib.MathLib;
using RaytracerLib.Resamplers;
using TomanuExtensions;
using YAXLib;
using RaytracerLib.Layers.Interpolators;
using RaytracerLib.Layers.OverlayCorrectors;
using System.Collections.Generic;

namespace RaytracerLib.Layers
{
    public abstract class ArrayBase : Layer
    {
        [YAXNode]
        public double TangentsPixelDelta = 0.1;

        [YAXNode]
        public double TangentsScale = 5;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private int m_width;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private int m_height;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private OverlayMethod m_overlay_method;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private InterpolationMethod m_intepolation_method;

        protected ArrayBase(OverlayMethod a_overlayMethod,
            InterpolationMethod a_interpolationMethod)
        {
            OverlayMethod = a_overlayMethod;
            InterpolationMethod = a_interpolationMethod;
        }

        private Interpolator m_interpolator;

        public int Width
        {
            get
            {
                return m_width;
            }
            protected set
            {
                m_width = value;
                m_interpolator = Interpolator.Create(this);
            }
        }

        public int Height
        {
            get
            {
                return m_height;
            }
            protected set
            {
                m_height = value;
                m_interpolator = Interpolator.Create(this);
            }
        }

        public override string ToString()
        {
            return String.Format("{0}; Width: {1}; Height: {2}",
                GetType().Name, Width, Height);
        }

        public override int GetHashCode()
        {
            return Height.GetHashCode() ^ Width.GetHashCode() ^
                OverlayMethod.GetHashCode() ^ InterpolationMethod.GetHashCode() ^
                TangentsPixelDelta.GetHashCode() ^ TangentsScale.GetHashCode();
        }

        [YAXNode]
        public OverlayMethod OverlayMethod
        {
            get
            {
                return m_overlay_method;
            }
            set
            {
                m_overlay_method = value;

                if ((m_width != 0) && (m_height != 0))
                    m_interpolator = Interpolator.Create(this);
            }
        }

        [YAXNode]
        public InterpolationMethod InterpolationMethod
        {
            get
            {
                return m_intepolation_method;
            }
            set
            {
                m_intepolation_method = value;

                if ((m_width != 0) && (m_height != 0))
                    m_interpolator = Interpolator.Create(this);
            }
        }

        public override ColorFloat GetColor(Vector2 a_uv, Intersection a_intersection)
        {
            double u = m_interpolator.OverlayCorrector.CorrectUV(a_uv.X);
            double v = m_interpolator.OverlayCorrector.CorrectUV(a_uv.Y);

            double fx = u * Width;
            double fy = v * Height;

            return GetColor(fx, fy);
        }

        public IEnumerable<Point> Pixels()
        {
            return new Rectangle(0, 0, Width, Height).EnumPixels();
        }

        public ColorFloat GetColor(double a_x, double a_y)
        {
            return m_interpolator.InterpolateColor(a_x, a_y);
        }

        public override double GetValue(Vector2 a_uv, Intersection a_intersection)
        {
            double u = m_interpolator.OverlayCorrector.CorrectUV(a_uv.X);
            double v = m_interpolator.OverlayCorrector.CorrectUV(a_uv.Y);

            double fx = u * Width;
            double fy = v * Height;

            return m_interpolator.InterpolateValue(fx, fy);
        }

        public override Tangents GetTangents(Vector2 a_uv, Vector2 a_tx, Vector2 a_ty,
            Intersection a_intersection)
        {
            double dx = TangentsPixelDelta / Width;
            double dy = TangentsPixelDelta / Height;

            double xyt = GetValue(a_uv, a_intersection);
            double xt = GetValue(a_uv + a_tx * dx, a_intersection);
            double yt = GetValue(a_uv + a_ty * dy, a_intersection);

            return new Tangents(new Vector2((xt - xyt) * TangentsScale,  TangentsPixelDelta).Normalized,
                                new Vector2((yt - xyt) * TangentsScale,  TangentsPixelDelta).Normalized);
        }

        public override bool Equals(object a_obj)
        {
            if (Object.ReferenceEquals(a_obj, this))
                return true;

            if (!base.Equals(a_obj))
                return false;

            ArrayBase ar = a_obj as ArrayBase;
            if (ar == null)
                return false;

            if (Width != ar.Width)
                return false;
            if (Height != ar.Height)
                return false;
            if (TangentsScale != ar.TangentsScale)
                return false;
            if (TangentsPixelDelta != ar.TangentsPixelDelta)
                return false;

            return true;
        }

        public abstract void Fill(ColorFloat a_color);
        public abstract void Fill(double a_value);

        public abstract ColorFloat GetColor(int a_x, int a_y);
        public abstract void SetColor(int a_x, int a_y, ColorFloat a_color);

        public abstract double GetValue(int a_x, int a_y);
        public abstract void SetValue(int a_x, int a_y, double a_value);
    }
}